using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
using TShooter.TfsWord.Resources;
using TShooter.TfsWord.Win.WizardSupport.Design;

namespace TShooter.TfsWord.Win.WizardSupport
{
    /// <summary>
    /// A wizard is the control added to a form to provide a step by step functionality.
    /// It contains <see cref="WizardPage"/>s in the <see cref="Pages"/> collection, which
    /// are containers for other controls. Only one wizard page is shown at a time in the client
    /// are of the wizard.
    /// </summary>
    [Designer(typeof(WizardDesigner))]
    [ToolboxItem(true)]
    [ToolboxBitmap(typeof(Wizard))]
    public partial class Wizard : UserControl
    {
        #region Constructors and member variables


        #region Events

        /// <summary>
        /// Called when the cancel button is pressed, before the form is closed. Set e.Cancel to true if 
        /// you do not wish the cancel to close the wizard.
        /// </summary>
        public event CancelEventHandler CloseFromCancel;

        /// <summary>
        /// Called when the Finish button is pressed, before the form is closed. Set e.Cancel to true if 
        /// you do not wish the Finish to close the wizard.
        /// </summary>
        public event CancelEventHandler CloseFromFinish;

        #endregion

        private readonly PageCollection vPages;

        protected internal Button btnBack;
        private Button btnCancel;
        protected internal Button btnNext;
        private Panel pnlButtonBright3d;
        private Panel pnlButtonDark3d;
        protected internal GripPanel pnlButtons;
        private WizardPage vActivePage;

        /// <summary>
        /// Wizard control with designer support
        /// </summary>
        public Wizard()
        {
            //Empty collection of Pages
            vPages = new PageCollection(this);

            // This call is required by the Windows.Forms Form Designer.
            InitializeComponent();

            InitializeStrings();
        }

        private void InitializeStrings()
        {
            btnNext.Text = Strings.Next;
            btnBack.Text = Strings.Back;
            btnCancel.Text = Strings.Cancel;
        }

        #endregion

        #region Event handlers

        private void Wizard_Load(object sender, EventArgs e)
        {
            //Attempt to activate a page
            ActivatePage(0);

            //Can I add my self as default cancel button
            Form form = FindForm();
            if (form != null && DesignMode == false)
            {
                form.CancelButton = btnCancel;
            }
        }

        private void btnNext_Click(object sender, EventArgs e)
        {
            Next();
        }

        private void btnNext_MouseDown(object sender, MouseEventArgs e)
        {
            if (DesignMode)
            {
                Next();
            }
        }

        private void btnBack_Click(object sender, EventArgs e)
        {
            Back();
        }

        private void btnBack_MouseDown(object sender, MouseEventArgs e)
        {
            if (DesignMode)
            {
                Back();
            }
        }

        private void btnCancel_Click(object sender, EventArgs e)
        {
            bool cancel = OnCancel();

            //If nobody told me to cancel
            if (!cancel)
            {
                //Then we close the form
                Form form = FindForm();
                if (form != null) form.Close();
            }
        }

        #endregion

        #region Properties

        /// <summary>
        /// Gets/Sets the activePage in the wizard
        /// </summary>
        [Category("Wizard")]
        internal int PageIndex
        {
            get { return vPages.IndexOf(vActivePage); }
            set
            {
                //Do I have any pages?
                if (vPages.Count == 0)
                {
                    //No then show nothing
                    ActivatePage(-1);
                    return;
                }
                // Validate the page asked for
                if (value < -1 || value >= vPages.Count)
                {
                    throw new ArgumentOutOfRangeException("The page index must be between 0 and " +
                                                          Convert.ToString(vPages.Count - 1));
                }
                //Select the new page
                ActivatePage(value);
            }
        }

        /// <summary>
        /// Alternative way of getting/Setiing  the current page by using wizardPage objects
        /// </summary>
        public WizardPage Page
        {
            get { return vActivePage; }
            //Dont use this anymore, see Next, Back, NextTo and BackTo
//			set
//			{
//				ActivatePage(value);
//			}
        }

        /// <summary>
        /// Gets/Sets the enabled state of the Next button. 
        /// </summary>
        [Category("Wizard")]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public bool NextEnabled
        {
            get { return btnNext.Enabled; }
            set { btnNext.Enabled = value; }
        }

        /// <summary>
        /// Gets/Sets the enabled state of the back button. 
        /// </summary>
        [Category("Wizard")]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public bool BackEnabled
        {
            get { return btnBack.Enabled; }
            set { btnBack.Enabled = value; }
        }

        /// <summary>
        /// Gets/Sets the enabled state of the cancel button. 
        /// </summary>
        [Category("Wizard")]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public bool CancelEnabled
        {
            get { return btnCancel.Enabled; }
            set { btnCancel.Enabled = value; }
        }

        public SizeGripStyle SizeGripStyle
        {
            get { return pnlButtons.SizeGripStyle; }
            set { pnlButtons.SizeGripStyle = value; }
        }

        /// <summary>
        /// Returns the collection of Pages in the wizard
        /// </summary>
        [Category("Wizard")]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        public PageCollection Pages
        {
            get { return vPages; }
        }

        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        [Browsable(false)]
        public Button BackButton
        {
            get { return btnBack; }
        }

        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        [Browsable(false)]
        public Button NextButton
        {
            get { return btnNext; }
        }

        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        [Browsable(false)]
        public Button CancelButton
        {
            get { return btnCancel; }
        }

        #endregion

        #region Overrides

        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);

            if (!DesignMode) return;

            const string noPagesText = "No wizard pages inside the wizard.";


            var textSize = e.Graphics.MeasureString(noPagesText, Font);
            var layout = new RectangleF((Width - textSize.Width)/2,
                                               (pnlButtons.Top - textSize.Height)/2,
                                               textSize.Width, textSize.Height);

            var dashPen = (Pen) SystemPens.GrayText.Clone();
            dashPen.DashStyle = DashStyle.Dash;

            e.Graphics.DrawRectangle(dashPen,
                                     Left + 8, Top + 8,
                                     Width - 17, pnlButtons.Top - 17);

            e.Graphics.DrawString(noPagesText, Font, new SolidBrush(SystemColors.GrayText), layout);
        }

        protected override void OnResize(EventArgs e)
        {
            base.OnResize(e);

            if (DesignMode)
            {
                Invalidate();
            }
        }

        #endregion

        #region Public methods

        public void ActivatePage(int index)
        {
            //If the new page is invalid
            if (index < 0 || index >= vPages.Count)
            {
                btnNext.Enabled = false;
                btnBack.Enabled = false;

                return;
            }


            //Change to the new Page
            WizardPage tWizPage = vPages[index];

            //Really activate the page
            ActivatePage(tWizPage);
        }

        public void ActivatePage(WizardPage page)
        {
            //Deactivate the current
            if (vActivePage != null)
            {
                vActivePage.Visible = false;
            }


            //Activate the new page
            vActivePage = page;

            if (vActivePage != null)
            {
                //Ensure that this panel displays inside the wizard
                vActivePage.Parent = this;
                if (Contains(vActivePage) == false)
                {
                    Container.Add(vActivePage);
                }
                //Make it fill the space
                vActivePage.Dock = DockStyle.Fill;
                vActivePage.Visible = true;
                vActivePage.BringToFront();
                vActivePage.FocusFirstTabIndex();
            }

            //What should the back button say
            btnBack.Enabled = PageIndex > 0;

            //What should the Next button say
            if (vActivePage == null) return;

            if (vPages.IndexOf(vActivePage) < vPages.Count - 1
                && vActivePage.IsFinishPage == false)
            {
                btnNext.Text = Strings.Next;
                btnNext.Enabled = true;
                //Don't close the wizard :)
                btnNext.DialogResult = DialogResult.None;
            }
            else
            {
                btnNext.Text = Strings.Finish;
                //Dont allow a finish in designer
                if (DesignMode && vPages.IndexOf(vActivePage) == vPages.Count - 1)
                {
                    btnNext.Enabled = false;
                }
                else
                {
                    btnNext.Enabled = true;
                    //If Not in design mode then allow a close
                    btnNext.DialogResult = DialogResult.OK;
                }
            }

            //Cause a refresh
            if (vActivePage != null)
            {
                vActivePage.Invalidate();
            }
            else
            {
                Invalidate();
            }
        }


        /// <summary>
        /// Closes the current page after a <see cref="WizardPage.CloseFromNext"/>, then moves to 
        /// the Next page and calls <see cref="WizardPage.ShowFromNext"/>
        /// </summary>
        public void Next()
        {
            Debug.Assert(PageIndex >= 0, Strings.PageIndexBelowZero);
            //Tell the Application I just closed a Page
            int newPage = vActivePage.OnCloseFromNext(this);

            //Did I just press Finish instead of Next
            if (PageIndex < vPages.Count - 1
                && (vActivePage.IsFinishPage == false || DesignMode))
            {
                //No still going
                ActivatePage(newPage);
                //Tell the application, I have just shown a page
                vActivePage.OnShowFromNext(this);
            }
            else
            {
                Debug.Assert(PageIndex < vPages.Count, Strings.GotPastFinish,
                             string.Format(Strings.GotPastFinishDetail, Convert.ToString(PageIndex + 1), Convert.ToString(vPages.Count)));
                //yep Finish was pressed
                if (DesignMode == false)
                {
                    bool cancel = OnFinish();
                    if (!cancel && ParentForm != null)
                    {
                        ParentForm.DialogResult = DialogResult.OK;
                        ParentForm.Close();
                    }
                }
            }
        }

        /// <summary>
        /// Moves to the page given and calls <see cref="WizardPage.ShowFromNext"/>
        /// </summary>
        /// <remarks>Does NOT call <see cref="WizardPage.CloseFromNext"/> on the current page</remarks>
        /// <param name="page"></param>
        public void NextTo(WizardPage page)
        {
            //Since we have a page to go to, then there is no need to validate most of it
            ActivatePage(page);
            //Tell the application, I have just shown a page
            vActivePage.OnShowFromNext(this);
        }

        /// <summary>
        /// Closes the current page after a <see cref="WizardPage.CloseFromBack"/>, then moves to 
        /// the previous page and calls <see cref="WizardPage.ShowFromBack"/>
        /// </summary>
        public void Back()
        {
            Debug.Assert(PageIndex < vPages.Count, Strings.PageIndexBeyondMax);
            //Can I press back
            Debug.Assert(PageIndex > 0 && PageIndex < vPages.Count, Strings.CannotGoBack);
            //Tell the application that I closed a page
            int newPage = vActivePage.OnCloseFromBack(this);

            ActivatePage(newPage);
            //Tell the application I have shown a page
            vActivePage.OnShowFromBack(this);
        }

        /// <summary>
        /// Moves to the page given and calls <see cref="WizardPage.ShowFromBack"/>
        /// </summary>
        /// <remarks>Does NOT call <see cref="WizardPage.CloseFromBack"/> on the current page</remarks>
        /// <param name="page"></param>
        public void BackTo(WizardPage page)
        {
            //Since we have a page to go to, then there is no need to validate most of it
            ActivatePage(page);
            //Tell the application, I have just shown a page
            vActivePage.OnShowFromNext(this);
        }

        #endregion

        #region Private methods

        private bool OnCancel()
        {
            var arg = new CancelEventArgs();

            //Throw the event out to subscribers
            if (CloseFromCancel != null)
            {
                CloseFromCancel(this, arg);
            }
            return arg.Cancel;
        }

        private bool OnFinish()
        {
            var arg = new CancelEventArgs();

            //Throw the event out to subscribers
            if (CloseFromFinish != null)
            {
                CloseFromFinish(this, arg);
            }
            return arg.Cancel;
        }

        #endregion

    }
}